/*
	IST_setup_64.c

	contains code responsible for registering ISRs with the system

	Author: Aidan Goddard 6/5/13
*/

#include"../kernel/headers/types.h"
#include"../kernel/headers/system_config_tables.h"
#include"headers/ISR.h"
#include"headers/printf.h"

typedef struct
{
	uint16_t target_offset_low;
	uint16_t target_selector;
	uint8_t IST_selector : 3;
	uint8_t reserved_1 : 5;
	uint8_t type : 4;
	uint8_t zero : 1;
	uint8_t DPL : 2;
	uint8_t present : 1;
	uint16_t target_offset_mid;
	uint32_t target_offset_high;
	uint32_t reserved_2;
}__attribute__((__packed__)) IDT64;


/*
	RegisterISR

	param 1 - vector number
	param 2 - ISR address (virtual)
	param 3 - selector to use (nearly always 0x08 - kernel code)
	param 4 - required cpl. 0 for IRQs, 3 for usermode INTs
	param 5 - gate type. either GATE_TRAP or GATE_INTERRUPT. difference is that GATE_INTERRUPT clears the Interrupts enabled bit
	param 6 - which stack to use. IST_1..7 or IST_NO_STACK

	Checks that the asking vector is not already registered,
	if it is, it returns 1. Otherwise it then checks if the
	required stack is sane (sometimes happens that it's not),
	if not it returns 2. Otherwise, it will set up the ISR
	with the requested information and return 0 (no error).

	returns:
	0 = No error occured.
	1 = ISR already mapped to vector. use DisableISR to remove it first.
	2 = Given stack number is not sane
*/
int RegisterISR(unsigned char vector, uint64_t address, unsigned short selector, int cpl, unsigned char gate_type, unsigned char stack)
{
	// display the information
	//Printf("\n[ISR REG] registering ISR %u to address 0x%a using selector 0x%x", vector, address, selector);

	// get a pointer to the IDT
	IDT64 *idt = (IDT64*)IDT_KOFFSET;

	// check entry is not already used
	if(idt[vector].present == 1)
	{
		return 1;
	}

	// check given cpl is sane
	if(cpl > 3)
	{
		cpl = 3;
	}

	// check stack number is sane
	if(stack > 7)
	{
		return 2;
	}

	// entry is not used, set it up
	idt[vector].target_selector = selector;
	idt[vector].DPL = cpl;
	idt[vector].type = gate_type;
	idt[vector].zero = 0;
	idt[vector].IST_selector = stack;
	idt[vector].reserved_1 = 0;
	idt[vector].reserved_2 = 0;
	idt[vector].target_offset_low = address & 0xffff;
	idt[vector].target_offset_mid = (address >> 16) & 0xffff;
	idt[vector].target_offset_high = (address >> 32) & 0xffffffff;

	// set present bit last
	idt[vector].present = 1;

	// get pointer to the system config table
	TBL_SYSCONFIG *tbl = (TBL_SYSCONFIG*)SYS_KOFFSET;

	// set the ISR vector to reserved by system
	tbl->tbl_isr_reservations[vector].is_reserved = 1;
	tbl->tbl_isr_reservations[vector].owner_id = 0xffffffff;

	// finished, return no error
	return 0;
}

/*
	DisableISR

	param 1 - vector of ISR to disable

	Simply marks the vector as not present within
	the system IDT.
*/
int DisableISR(unsigned char vector)
{
	// print info
	//Printf("\n[ISR REG] disabling ISR %u", vector);

	// get a pointer to the IDT
	IDT64 *idt = (IDT64*)IDT_KOFFSET;

	// clear present bit
	idt[vector].present = 0;

	// get pointer to the system config table
	TBL_SYSCONFIG *tbl = (TBL_SYSCONFIG*)SYS_KOFFSET;

	// set the ISR vector to available
	tbl->tbl_isr_reservations[vector].is_reserved = 0;
	tbl->tbl_isr_reservations[vector].owner_id = 0;


	return 0;
}
